#include "common.h"
#include "tcpci_event.h"
#include "tcpci.h"
#include "tcpci_core.h"
#include "tcpci_timer.h"
#include "tcpm.h"

#undef LOG_TAG
#define LOG_TAG    "tcpci_type"
#include "elog.h"

#if (CONFIG_TYPEC_CAP_TRY_SOURCE || CONFIG_TYPEC_CAP_TRY_SINK)
#define CONFIG_TYPEC_CAP_TRY_STATE 1
#else
#define CONFIG_TYPEC_CAP_TRY_STATE 0
#endif

#if TYPEC_ROLE_INFO
static const char *const typec_role_name[] = {
	"UNKNOWN",
	"SNK",
	"SRC",
	"DRP",
	"TrySRC",
	"TrySNK",
};
#endif /* TYPEC_ROLE_INFO */

enum TYPEC_WAIT_PS_STATE {
	TYPEC_WAIT_PS_DISABLE = 0,
	TYPEC_WAIT_PS_SNK_VSAFE5V,
	TYPEC_WAIT_PS_SRC_VSAFE0V,
	TYPEC_WAIT_PS_SRC_VSAFE5V,
	TYPEC_WAIT_PS_DBG_VSAFE5V,
};

enum TYPEC_ROLE_SWAP_STATE {
	TYPEC_ROLE_SWAP_NONE = 0,
	TYPEC_ROLE_SWAP_TO_SNK,
	TYPEC_ROLE_SWAP_TO_SRC,
};

#if TYPEC_PS_INFO
static const char *const typec_wait_ps_name[] = {
	"Disable",
	"SNK_VSafe5V",
	"SRC_VSafe0V",
	"SRC_VSafe5V",
	"DBG_VSafe5V",
};
#endif	/* TYPEC_PS_INFO */

#if TYPEC_STATE_INFO
static const char *const typec_state_name[] = {
	"Disabled",
	"ErrorRecovery",

	"Unattached.SNK",
	"Unattached.SRC",

	"AttachWait.SNK",
	"AttachWait.SRC",

	"Attached.SNK",
	"Attached.SRC",

#if CONFIG_TYPEC_CAP_TRY_SOURCE
	"Try.SRC",
	"TryWait.SNK",
	"TryWait.SNK.PE",
#endif	/* CONFIG_TYPEC_CAP_TRY_SOURCE */

#if CONFIG_TYPEC_CAP_TRY_SINK
	"Try.SNK",
	"TryWait.SRC",
	"TryWait.SRC.PE",
#endif	/* CONFIG_TYPEC_CAP_TRY_SINK */

	"AudioAccessory",
#if CONFIG_TYPEC_CAP_DBGACC
	"DebugAccessory",
#endif
#if CONFIG_TYPEC_CAP_DBGACC_SNK
	"DBGACC.SNK",
#endif	/* CONFIG_TYPEC_CAP_DBGACC_SNK */

#if CONFIG_TYPEC_CAP_CUSTOM_SRC
	"Custom.SRC",
#endif	/* CONFIG_TYPEC_CAP_CUSTOM_SRC */

#if CONFIG_TYPEC_CAP_NORP_SRC
	"NoRp.SRC",
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */

#if CONFIG_TYPEC_CAP_ROLE_SWAP
	"RoleSwap",
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

#if CONFIG_WATER_DETECTION
	"WaterProtection",
#endif /* CONFIG_WATER_DETECTION */

	"ForeignObjectProtection",
	"TypeC.OTP",
	"UnattachWait.PE",
};
#endif // TYPEC_STATE_INFO

static inline void typec_transfer_state(struct sgm41620_tcpc *tcpc_dev,
					enum TYPEC_CONNECTION_STATE state)
{
#if TYPEC_STATE_INFO
	if (state > 0 && state < ARRAY_SIZE(typec_state_name))
		printf("[%d]** %s\r\n", g_sys_ticks, typec_state_name[state]);
#endif
	tcpc_dev->typec_state = (uint8_t) state;
}

#define TYPEC_NEW_STATE(state)  \
	(typec_transfer_state(p_tcpc_dev, state))

#if TYPEC_STATE_INFO
static const char *const typec_attach_names[] = {
	"NULL",
	"SINK",
	"SOURCE",
	"AUDIO",
	"DEBUG",
	"DBGACC_SNK",
	"CUSTOM_SRC",
	"NORP_SRC",
	"PROTECTION",
};
#endif

static inline void typec_wait_ps_change(struct sgm41620_tcpc *tcpc_dev,
					enum TYPEC_WAIT_PS_STATE state)
{
#if TYPEC_PS_INFO
	uint8_t old_state = tcpc_dev->typec_wait_ps_change;
	uint8_t new_state = (uint8_t) state;

	if (new_state != old_state)
		printf("[%d]%s\n", g_sys_ticks, typec_wait_ps_name[new_state]);
#endif	/* TYPEC_PS_INFO */

#if CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
	if (state == TYPEC_WAIT_PS_SRC_VSAFE0V){
		tcpci_enable_force_discharge(tcpc_dev, true, 0);
		tcpc_enable_timer(tcpc_dev, TYPEC_RT_TIMER_SAFE0V_TOUT);
	}
#endif	/* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */

	if (tcpc_dev->typec_wait_ps_change == TYPEC_WAIT_PS_SRC_VSAFE0V
		&& state != TYPEC_WAIT_PS_SRC_VSAFE0V) {
#if CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY
		tcpc_disable_timer(tcpc_dev, TYPEC_RT_TIMER_SAFE0V_DELAY);
		log_i("tcpc_disable TYPEC_RT_TIMER_SAFE0V_DELAY \r\n"); 
#endif
#if CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
		tcpc_disable_timer(p_tcpc_dev, TYPEC_RT_TIMER_SAFE0V_TOUT);
		tcpci_enable_force_discharge(tcpc_dev, true, 0);
		//log_i("tcpc_disable_timer TYPEC_RT_TIMER_SAFE0V_TOUT \r\n");
#endif	/* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */
	}

	tcpc_dev->typec_wait_ps_change = (uint8_t) state;
}

int tcpci_set_cc(struct sgm41620_tcpc *tcpc, int pull)
{
    if (p_tcpc_dev->ops->set_cc == NULL)
        return -1;

    if (pull & TYPEC_CC_DRP) {
        tcpc->typec_remote_cc[0] =
        tcpc->typec_remote_cc[1] =
            TYPEC_CC_DRP_TOGGLING;
    }
    tcpc->typec_local_cc = pull;
    return p_tcpc_dev->ops->set_cc(pull);
}

static inline int typec_enable_vconn(struct sgm41620_tcpc *tcpc)
{
	if (!typec_is_sink_with_emark())
		return 0;

	if (tcpc->tcpc_vconn_supply == TCPC_VCONN_SUPPLY_NEVER)
		return 0;

	//return 0;
	return tcpci_set_vconn(tcpc, true);
}

int tcpci_report_usb_port_attached(struct sgm41620_tcpc *tcpc)
{
	//log_i("usb_port_attached\n");

#if CONFIG_USB_PD_DISABLE_PE
	if (tcpc->disable_pe)
		return 0;
#endif	/* CONFIG_USB_PD_DISABLE_PE */

	/* MTK Only */
	if (tcpc->pd_inited_flag){
		//log_i("before cc_attached_event222\r\n");
		pd_put_cc_attached_event(tcpc, tcpc->typec_attach_new);
	}

	return 0;
}

int tcpci_report_usb_port_detached(struct sgm41620_tcpc *tcpc)
{
	//log_i("usb_port_detached\n");

	/* MTK Only */
	if (tcpc->pd_inited_flag)
		pd_put_cc_detached_event(tcpc);
	else {
		pd_event_buf_reset(tcpc);
		tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_PE_IDLE);
		log_i("tcpc_enable_timer TYPEC_RT_TIMER_PE_IDLE\n");
	}

	return 0;
}


int tcpci_report_usb_port_changed(struct sgm41620_tcpc *tcpc)
{
	//tcpci_notify_typec_state(tcpc);
	//log_i("tcpci_report_usb_port_changed\n");
	if (tcpc->typec_attach_new == TYPEC_UNATTACHED ||
	    tcpc->typec_attach_new == TYPEC_PROTECTION)
		tcpci_report_usb_port_detached(tcpc);
	else if (tcpc->typec_attach_old == TYPEC_UNATTACHED)
		tcpci_report_usb_port_attached(tcpc);
	else
		log_i("TCPC Attach Again\n");

	return 0;
}


static int typec_alert_attach_state_change(struct sgm41620_tcpc *tcpc)
{
	int ret = 0;
	//log_i("typec_alert_attach_state_change \n");
	if (tcpc->typec_attach_old == tcpc->typec_attach_new) {
#if TYPEC_STATE_INFO
		log_i("Attached-> %s(repeat)\n",
			typec_attach_names[tcpc->typec_attach_new]);
#endif
		return 0;
	}

#if TYPEC_STATE_INFO
	printf("[%d]typec %s\n", g_sys_ticks, typec_attach_names[tcpc->typec_attach_new]);
#endif

	/* Report function */
	ret = tcpci_report_usb_port_changed(tcpc);

	tcpc->typec_attach_old = tcpc->typec_attach_new;
	return ret;
}

static inline void typec_enable_low_power_mode(struct sgm41620_tcpc *tcpc)
{
    tcpc->typec_lpm = true;
    tcpc_enable_lpm_timer(tcpc, true);
}

static inline int typec_disable_low_power_mode(struct sgm41620_tcpc *tcpc)
{
    tcpc_enable_lpm_timer(tcpc, false);
    return tcpci_set_low_power_mode(tcpc, false);
}

/*
 * [BLOCK] NoRpSRC Entry
 */

#if CONFIG_TYPEC_CAP_NORP_SRC
static void typec_unattach_wait_pe_idle_entry(struct sgm41620_tcpc *tcpc);
static bool typec_try_norp_src(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_state == typec_unattached_snk) {
		if (tcpci_check_vbus_valid(tcpc) &&
		    typec_is_cc_no_res()) {
			log_i("norp_src=1\n");
			tcpc_enable_timer(tcpc, TYPEC_TIMER_NORP_SRC);
			return true;
		}
		//log_i("disable norp_src timer\n");
		tcpc_disable_timer(tcpc, TYPEC_TIMER_NORP_SRC);
	}

	if (tcpc->typec_state == typec_attached_norp_src) {
		if (tcpci_check_vbus_valid(tcpc) &&
		    typec_is_cc_no_res()) {
			log_i("keep norp_src\n");
		} else {
			log_i("norp_src=0\n");
			typec_unattach_wait_pe_idle_entry(tcpc);
			typec_alert_attach_state_change(tcpc);
		}
		return true;
	}
	return false;
}

static inline void typec_norp_src_attached_entry(struct sgm41620_tcpc *tcpc)
{
	typec_disable_low_power_mode(tcpc);

	TYPEC_NEW_STATE(typec_attached_norp_src);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_NORP_SRC;

	tcpci_set_cc(tcpc, TYPEC_CC_RD);
	tcpci_report_power_control(tcpc, true);
	tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, 500);
}
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */


static inline int typec_enter_low_power_mode(struct sgm41620_tcpc *tcpc)
{
    int ret = 0;

    if (!tcpc->typec_lpm)
        return 0;

    ret = tcpci_set_low_power_mode(tcpc, true);
    if (ret < 0)
        tcpc_enable_lpm_timer(tcpc, true);

    return ret;
}



#if CONFIG_TYPEC_CAP_TRY_SINK
static inline int typec_handle_try_sink_cc_change(
	struct sgm41620_tcpc *tcpc)
{
	/*
	 * The port shall wait for tDRPTry and only then begin
	 * monitoring the CC1 and CC2 pins for the SNK.Rp state
	 */

	if (!tcpc->typec_drp_try_timeout) {
		log_i("[Try.SNK] Ignore CC_Alert\n");
		return 1;
	}

	if (!typec_is_cc_open()) {
		log_i("tcpci_notify_attachwait_state\n");
		//tcpci_notify_attachwait_state(tcpc, true);
		return 0;
	}

	return 0;
}
#endif	/* CONFIG_TYPEC_CAP_TRY_SINK */

static inline int typec_get_rp_present_flag(struct sgm41620_tcpc *tcpc_dev)
{
	uint8_t rp_flag = 0;

	if (tcpc_dev->typec_remote_cc[0] >= TYPEC_CC_VOLT_SNK_DFT
		&& tcpc_dev->typec_remote_cc[0] != TYPEC_CC_DRP_TOGGLING)
		rp_flag |= 1;

	if (tcpc_dev->typec_remote_cc[1] >= TYPEC_CC_VOLT_SNK_DFT
		&& tcpc_dev->typec_remote_cc[1] != TYPEC_CC_DRP_TOGGLING)
		rp_flag |= 2;

	return rp_flag;
}

bool tcpc_typec_is_cc_open_state(struct sgm41620_tcpc *tcpc_dev)
{
	if (tcpc_dev->typec_state == typec_errorrecovery)
		return true;

	if (tcpc_dev->typec_state == typec_disabled)
		return true;
    
	return false;
}


static inline bool typec_is_ignore_cc_change(
	struct sgm41620_tcpc *tcpc_dev, uint8_t rp_present)
{

	if (typec_is_drp_toggling())
		return true;
	
	if (tcpc_typec_is_cc_open_state(tcpc_dev)){
		log_i("tcpc_typec_is_cc_open_state\r\n");
		return true;
	}

#if 0
	if (tcpc_dev->typec_legacy_cable &&
		typec_legacy_handle_cc_change(tcpc_dev)) {
		log_i("TYPEC_CHECK_LEGACY_CABLE\r\n");
		return true;
	}
#endif	/* CONFIG_TYPEC_CHECK_LEGACY_CABLE */

#if 1//def CONFIG_USB_POWER_DELIVERY
	if (tcpc_dev->typec_state == typec_attachwait_snk &&
		typec_get_rp_present_flag(tcpc_dev) == rp_present) {
		log_i("[AttachWait] Ignore RpLvl Alert\r\n");
		return true;
	}

	if (tcpc_dev->pd_wait_pr_swap_complete) {
		log_i("[PR.Swap] Ignore CC_Alert\r\n");
		return true;
	}
#endif /* CONFIG_USB_POWER_DELIVERY */

#if CONFIG_TYPEC_CAP_TRY_SINK
	if (tcpc_dev->typec_state == typec_try_snk) {
		log_i("typec_try_snk\r\n");
		if (typec_handle_try_sink_cc_change(tcpc_dev) > 0)
			return true;
	}

	if (tcpc_dev->typec_state == typec_trywait_src_pe) {
		log_i("[Try.PE1] Ignore CC_Alert\r\n");
		return true;
	}
#endif	/* CONFIG_TYPEC_CAP_TRY_SINK */

#if CONFIG_TYPEC_CAP_TRY_SOURCE
	if (tcpc_dev->typec_state == typec_trywait_snk_pe) {
		log_i("[Try.PE2] Ignore CC_Alert\r\n");
		return true;
	}
#endif	/* CONFIG_TYPEC_CAP_TRY_SOURCE */
	//log_i("typec_is_ignore_cc_change return flase\r\n");
	return false;
}

int tcpci_get_cc(struct sgm41620_tcpc *tcpc)
{
	int ret;
	int cc1, cc2;

    if (p_tcpc_dev->ops->get_cc == NULL)
        return -1;

	ret = p_tcpc_dev->ops->get_cc(&cc1, &cc2);
	if (ret < 0)
		return ret;

	if ((cc1 == tcpc->typec_remote_cc[0]) &&
			(cc2 == tcpc->typec_remote_cc[1])) {
		return 0;
	}

	tcpc->typec_remote_cc[0] = cc1;
	tcpc->typec_remote_cc[1] = cc2;

	return 1;
}

int tcpci_get_power_status(struct sgm41620_tcpc *tcpc, uint16_t *pw_status)
{
	int ret;

    if (p_tcpc_dev->ops->get_power_status == NULL)
        return -1;

    ret = p_tcpc_dev->ops->get_power_status(pw_status);
    if (ret < 0)
        return ret;

    tcpci_vbus_level_init(tcpc, *pw_status);
    return 0;
}

int tcpci_init(struct sgm41620_tcpc *tcpc, bool sw_reset)
{
    uint16_t power_status = 0;
    int ret;

    if (tcpc->ops->init == NULL)
        return 0;

    ret = tcpc->ops->init(sw_reset);
    if (ret < 0)
        return ret;

    return tcpci_get_power_status(tcpc, &power_status);
}

int tcpci_check_vbus_valid_from_ic(struct sgm41620_tcpc *tcpc)
{
	//log_i("tcpci_check_vbus_valid_from_ic\r\n");
	uint16_t power_status;
	int vbus_level = tcpc->vbus_level;

	if (tcpci_get_power_status(tcpc, &power_status) == 0) {
		if (vbus_level != tcpc->vbus_level) {
			log_i("[Warning] ps_changed %d -> %d\n",
				vbus_level, tcpc->vbus_level);
		}
	}

	return tcpci_check_vbus_valid(tcpc);
}

int tcpci_is_plugged_in(struct sgm41620_tcpc *tcpc)
{
	log_i("tcpci_is_plugged_in\r\n");
	if (tcpci_check_vbus_valid_from_ic(tcpc))
		return 1;

	if (tcpci_get_cc(tcpc) < 0)
		return -1;

	if ((tcpc->typec_remote_cc[0] == TYPEC_CC_VOLT_OPEN ||
	     tcpc->typec_remote_cc[0] == TYPEC_CC_DRP_TOGGLING) &&
	    (tcpc->typec_remote_cc[1] == TYPEC_CC_VOLT_OPEN ||
	     tcpc->typec_remote_cc[1] == TYPEC_CC_DRP_TOGGLING))
		return 0;

	return 1;
}


int tcpci_get_message(struct sgm41620_tcpc *tcpc,
	uint32_t *payload, uint16_t *head, enum tcpm_transmit_type *type)
{
    if (p_tcpc_dev->ops->get_message)
        return p_tcpc_dev->ops->get_message(payload, head, type);
    else
        return -1;
}

static inline void typec_unattached_src_and_drp_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_unattached_src_and_drp_entry\r\n");
	TYPEC_NEW_STATE(typec_unattached_src);
	tcpci_set_cc(tcpc, TYPEC_CC_RP);
	tcpc_enable_timer(tcpc, TYPEC_TIMER_DRP_SRC_TOGGLE);
#if CONFIG_TYPEC_VBUS_CC_SHORT
	if (tcpc->typec_vbus_to_cc_en && tcpc->tcpc_flags & TCPC_FLAGS_VBUS_SHORT_CC){
		log_i("unattached_src_and_drp typec_vbus_to_cc_en\r\n");
		//tcpci_set_vbus_short_cc_en(tcpc, false, false);
	}
#endif
}

static inline void typec_unattached_snk_and_drp_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_unattached_snk_and_drp_entry\r\n");
	TYPEC_NEW_STATE(typec_unattached_snk);
	//tcpci_set_auto_dischg_discnt(tcpc, false);
	tcpci_set_cc(tcpc, TYPEC_CC_DRP);
	typec_enable_low_power_mode(tcpc);
#if CONFIG_TYPEC_VBUS_CC_SHORT
	if (tcpc->typec_vbus_to_cc_en && tcpc->tcpc_flags & TCPC_FLAGS_VBUS_SHORT_CC){
		log_i("unattached_src_and_drp typec_vbus_to_cc_en\r\n");
		//tcpci_set_vbus_short_cc_en(tcpc, false, false);
	}
#endif
}

#if 1
static inline void typec_unattached_cc_entry(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_TYPEC_CAP_ROLE_SWAP
	if (tcpc->typec_during_role_swap) {
		TYPEC_NEW_STATE(typec_role_swap);
		return;
	}
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

#if CONFIG_CABLE_TYPE_DETECTION
	if (tcpc->tcpc_flags & TCPC_FLAGS_CABLE_TYPE_DETECTION)
		tcpc_typec_handle_ctd(tcpc, TCPC_CABLE_TYPE_NONE);
#endif /* CONFIG_CABLE_TYPE_DETECTION */

	//to do
	if (!typec_is_cc_no_res()) {
		//log_i("typec_is_cc_no_res\r\n");
		tcpci_set_cc(tcpc, TYPEC_CC_OPEN);
		//delay_1ms(10);
	}

	tcpc->typec_role = tcpc->typec_role_new;
	//log_i("%s\r\n",__func__);
	switch (tcpc->typec_role) {
	case TYPEC_ROLE_SNK:
		TYPEC_NEW_STATE(typec_unattached_snk);
		//tcpci_set_auto_dischg_discnt(tcpc, false);
		log_i("%s :case TYPEC_ROLE_SNK\r\n",__func__);
		tcpci_set_cc(tcpc, TYPEC_CC_RD);
		typec_enable_low_power_mode(tcpc);
		break;
	case TYPEC_ROLE_SRC:
		TYPEC_NEW_STATE(typec_unattached_src);
		log_i("%s :case TYPEC_ROLE_SRC\r\n",__func__);
		tcpci_set_cc(tcpc, TYPEC_CC_RP);
		typec_enable_low_power_mode(tcpc);
		break;
	case TYPEC_ROLE_TRY_SRC:
		log_i("%s :case TYPEC_ROLE_TRY_SRC\r\n",__func__);
		if (tcpc->typec_state == typec_errorrecovery) {
			typec_unattached_src_and_drp_entry(tcpc);
			break;
		}
	default:
		switch (tcpc->typec_state) {
		case typec_attachwait_snk:
		case typec_audioaccessory:
			typec_unattached_src_and_drp_entry(tcpc);
			break;
		default:
			typec_unattached_snk_and_drp_entry(tcpc);
			break;
		}
		break;
	}
}
#endif


static void typec_unattached_power_entry(struct sgm41620_tcpc *tcpc_dev)
{
	typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);

	if (tcpc_dev->typec_power_ctrl) {
		tcpci_set_vconn(tcpc_dev, false);
		tcpci_disable_vbus_control(tcpc_dev);
		
	}
}


static void typec_unattached_entry(struct sgm41620_tcpc *tcpc)
{
	tcpc->typec_usb_sink_curr = CONFIG_TYPEC_SNK_CURR_DFT;
	//log_i("typec_unattached_entry\r\n");
	if (tcpc->typec_power_ctrl)
		tcpci_set_vconn(tcpc, false);
	typec_unattached_cc_entry(tcpc);
	typec_unattached_power_entry(tcpc);
}

static void typec_attach_new_unattached(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_attach_new_unattached\r\n");
	tcpc->typec_attach_new = TYPEC_UNATTACHED;
	tcpc->typec_remote_rp_level = TYPEC_CC_VOLT_SNK_DFT;
	tcpc->typec_polarity = false;
}

static void typec_unattach_wait_pe_idle_entry(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_unattach_wait_pe_idle_entry\r\n");
	typec_attach_new_unattached(tcpc);

	if (tcpc->pd_pe_running) {
		TYPEC_NEW_STATE(typec_unattachwait_pe);
		return;
	}

	typec_unattached_entry(tcpc);
}


static void typec_postpone_state_change(struct sgm41620_tcpc *tcpc)
{
	log_i("Postpone AlertChange\n");
	tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_STATE_CHANGE);
}

static bool typec_is_in_protection_states(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_WATER_DETECTION
	if (tcpc->typec_state == typec_water_protection)
		return true;
#endif /* CONFIG_WATER_DETECTION */

	if ((tcpc->tcpc_flags & TCPC_FLAGS_FOREIGN_OBJECT_DETECTION) &&
	    tcpc->typec_state == typec_foreign_object_protection)
		return true;

	if ((tcpc->tcpc_flags & TCPC_FLAGS_TYPEC_OTP) &&
	    tcpc->typec_state == typec_otp)
		return true;

	return false;
}

static void typec_cc_open_entry(struct sgm41620_tcpc *tcpc, enum TYPEC_CONNECTION_STATE state)
{
	typec_attach_new_unattached(tcpc);
	TYPEC_NEW_STATE(state);
	tcpci_set_cc(tcpc, TYPEC_CC_OPEN);
	if (tcpc->typec_state == typec_disabled)
		typec_enable_low_power_mode(tcpc);
	else{
		typec_disable_low_power_mode(tcpc);
	}
	typec_unattached_power_entry(tcpc);
	typec_alert_attach_state_change(tcpc);
	if (typec_is_in_protection_states(tcpc)) {
		tcpc->typec_attach_new = TYPEC_PROTECTION;
		typec_postpone_state_change(tcpc);
	}
}

static inline void typec_disable_entry(struct sgm41620_tcpc *tcpc)
{
	typec_cc_open_entry(tcpc, typec_disabled);
}

int tcpc_typec_disable(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_state != typec_disabled)
		typec_disable_entry(tcpc);

	return 0;
}

int tcpc_typec_enable(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_state == typec_disabled)
		typec_unattached_entry(tcpc);

	return 0;
}

int tcpc_typec_init(struct sgm41620_tcpc *tcpc, uint8_t typec_role)
{
	int ret = 0;
	if (typec_role == TYPEC_ROLE_UNKNOWN ||
		typec_role >= TYPEC_ROLE_NR) {
		log_i("init Wrong TypeC-Role: %d\n", typec_role);
		return -1;
	}

#if TYPEC_ROLE_INFO
	log_i("typec_init: %s\n", typec_role_name[typec_role]);
#endif

	tcpc->typec_role = typec_role;
	tcpc->typec_role_new = typec_role;
	typec_attach_new_unattached(tcpc);
	tcpc->typec_attach_old = TYPEC_UNATTACHED;

	ret = tcpci_get_cc(tcpc);

#if CONFIG_TYPEC_CAP_NORP_SRC
	if (!tcpci_check_vbus_valid(tcpc) || ret < 0)
		tcpc->typec_power_ctrl = true;
#else
	if (!tcpci_check_vbus_valid(tcpc) || ret < 0 || typec_is_cc_no_res())
		tcpc->typec_power_ctrl = true;
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */

	typec_unattached_entry(tcpc);
#if CONFIG_TYPEC_CAP_NORP_SRC
	if (typec_try_norp_src(tcpc))
		return 0;
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */
	return ret;
}

int tcpci_set_low_rp_duty(struct sgm41620_tcpc *tcpc, bool low_rp)
{
    if (tcpc->ops->set_low_rp_duty)
        return tcpc->ops->set_low_rp_duty(low_rp);

    return 0;
}

int tcpci_set_msg_header(struct sgm41620_tcpc *tcpc, uint8_t power_role, uint8_t data_role)
{
    if (tcpc->ops->set_msg_header)
        return tcpc->ops->set_msg_header(power_role, data_role);
    else
        return -1;
}

int tcpci_set_rx_enable(struct sgm41620_tcpc *tcpc, uint8_t enable)
{
    if (tcpc->ops->set_rx_enable)
        return tcpc->ops->set_rx_enable(enable);
    else
        return -1;
}

int tcpci_protocol_reset(struct sgm41620_tcpc *tcpc)
{
    if (tcpc->ops->protocol_reset)
        return tcpc->ops->protocol_reset();
    else
        return -1;
}


static inline bool typec_is_cc_attach(struct sgm41620_tcpc *tcpc_dev)
{
	bool cc_attach = false;
	int cc1 = typec_get_cc1();
	int cc2 = typec_get_cc2();
	int cc_res = typec_get_cc_res();


	switch (tcpc_dev->typec_state) {
	case typec_attached_snk:
	case typec_attached_src:
#if CONFIG_TYPEC_CAP_CUSTOM_SRC
	case typec_attached_custom_src:
#endif	/* CONFIG_TYPEC_CAP_CUSTOM_SRC */
#if CONFIG_TYPEC_CAP_DBGACC_SNK
	case typec_attached_dbgacc_snk:
#endif	/* CONFIG_TYPEC_CAP_DBGACC_SNK */
		if ((cc_res != TYPEC_CC_VOLT_OPEN) &&
				(cc_res != TYPEC_CC_VOLT_RA))
			cc_attach = true;
		break;
	case typec_audioaccessory:
		if (typec_check_cc_both(TYPEC_CC_VOLT_RA))
			cc_attach = true;
		break;

#if CONFIG_TYPEC_CAP_DBGACC
	case typec_debugaccessory:
		if (typec_check_cc_both(TYPEC_CC_VOLT_RD))
			cc_attach = true;
		break;
#endif	/* CONFIG_TYPEC_CAP_DBGACC */

	default:	/* TYPEC_UNATTACHED */
		if (cc1 != TYPEC_CC_VOLT_OPEN)
			cc_attach = true;

		if (cc2 != TYPEC_CC_VOLT_OPEN)
			cc_attach = true;

		/* Cable Only, no device */
		if ((cc1+cc2) == TYPEC_CC_VOLT_RA) {
			cc_attach = false;
			//log_i("[Cable] Ra Only\r\n");
		}
		break;
	}

	return cc_attach;
}


/*static inline bool typec_is_act_as_sink_role(
	struct sgm41620_tcpc *tcpc_dev)
{
	bool as_sink = true;
	uint8_t cc_sum;

	switch (TYPEC_CC_PULL_GET_RES(tcpc_dev->typec_local_cc)) {
	case TYPEC_CC_RP:
		as_sink = false;
		break;
	case TYPEC_CC_RD:
		as_sink = true;
		break;
	case TYPEC_CC_DRP:
		cc_sum = typec_get_cc1() + typec_get_cc2();
		as_sink = (cc_sum >= TYPEC_CC_VOLT_SNK_DFT);
		break;
	}

	return as_sink;
}*/

static inline int typec_set_polarity(struct sgm41620_tcpc *tcpc,
					bool polarity)
{
	tcpc->typec_polarity = polarity;
	return tcpci_set_polarity(tcpc, polarity);
}

static inline int typec_set_plug_orient(struct sgm41620_tcpc *tcpc,
				uint8_t pull, bool polarity)
{
	int ret = typec_set_polarity(tcpc, polarity);

	if (ret)
		return ret;

	//log_i("typec_set_plug_orient pull =%d\r\n",pull);
	return tcpci_set_cc(tcpc, pull);
}


static void typec_source_attached_with_vbus_entry(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_source_attached_with_vbus_entry\r\n");
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_SRC;
}

static inline void typec_source_attached_entry(struct sgm41620_tcpc *tcpc)
{
	TYPEC_NEW_STATE(typec_attached_src);
	tcpc->typec_is_attached_src = true;
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SRC_VSAFE5V);

	tcpc_disable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY);
	//log_i("typec_source_attached_entry\r\n");
#if CONFIG_TYPEC_CAP_ROLE_SWAP
	if (tcpc->typec_during_role_swap) {
		tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_NONE;
		tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP);
	}
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

	typec_set_plug_orient(tcpc,
		TYPEC_CC_PULL(tcpc->typec_local_rp_level, TYPEC_CC_RP),
		typec_check_cc2(TYPEC_CC_VOLT_RD));

	tcpci_report_power_control(tcpc, true);
	typec_enable_vconn(tcpc);
	tcpci_source_vbus(tcpc,
			TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, -1);
}

static inline void typec_sink_attached_entry(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_sink_attached_entry!\n");
	TYPEC_NEW_STATE(typec_attached_snk);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_SNK;

#if CONFIG_TYPEC_CAP_TRY_STATE
	if (tcpc->typec_role >= TYPEC_ROLE_DRP)
		tcpc_reset_typec_try_timer(tcpc);
#endif	/* CONFIG_TYPEC_CAP_TRY_STATE */

#if CONFIG_TYPEC_CAP_ROLE_SWAP
	if (tcpc->typec_during_role_swap) {
		tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_NONE;
		tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP);
	}
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

	typec_set_plug_orient(tcpc, TYPEC_CC_RD,
		!typec_check_cc2(TYPEC_CC_VOLT_OPEN));
	tcpc->typec_remote_rp_level = typec_get_cc_res();
	
	//log_i("polarity = %d, rp_level = %d!\n",p_tcpc_dev->typec_polarity,typec_get_cc_res());
	//tcpci_set_auto_dischg_discnt(tcpc, true);
	tcpci_report_power_control(tcpc, true);
	tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
#if CONFIG_TYPEC_VBUS_CC_SHORT
	if (!tcpc->typec_vbus_to_cc_en && tcpc->tcpc_flags & TCPC_FLAGS_VBUS_SHORT_CC) {
		if (tcpc->typec_polarity == TCPC_POLARITY_CC1){
			log_i("tcpci_set_vbus_short_cc_en false!\n");
			//tcpci_set_vbus_short_cc_en(tcpc, true, false);
		}
		else{
			log_i("tcpci_set_vbus_short_cc_en true!\n");
			//tcpci_set_vbus_short_cc_en(tcpc, false, true);
		}
	}
#endif
}


static inline void typec_custom_src_attached_entry(
	struct sgm41620_tcpc *tcpc)
{
#if CONFIG_TYPEC_CAP_DBGACC_SNK
	log_i("[Warning] Same Rp (%d)\n", typec_get_cc1());
#else
	log_i("[Warning] CC Both Rp\n");
#endif

#if CONFIG_TYPEC_CAP_CUSTOM_SRC
	TYPEC_NEW_STATE(typec_attached_custom_src);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_CUSTOM_SRC;

	tcpc->typec_remote_rp_level = typec_get_cc1();

	tcpci_report_power_control(tcpc, true);
	tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
	if (!tcpc->typec_vbus_to_cc_en && tcpc->tcpc_flags & TCPC_FLAGS_VBUS_SHORT_CC)
		tcpci_set_vbus_short_cc_en(tcpc, true, true);
#endif	/* CONFIG_TYPEC_CAP_CUSTOM_SRC */
}


#if CONFIG_TYPEC_CAP_DBGACC_SNK

static inline uint8_t typec_get_sink_dbg_acc_rp_level(
	int cc1, int cc2)
{
	if (cc2 == TYPEC_CC_VOLT_SNK_DFT)
		return cc1;

	return TYPEC_CC_VOLT_SNK_DFT;
}

static inline void typec_sink_dbg_acc_attached_entry(
	struct sgm41620_tcpc *tcpc)
{
	bool polarity;
	uint8_t rp_level;

	uint8_t cc1 = typec_get_cc1();
	uint8_t cc2 = typec_get_cc2();

	if (cc1 == cc2) {
		typec_custom_src_attached_entry(tcpc);
		return;
	}

	TYPEC_NEW_STATE(typec_attached_dbgacc_snk);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_DBGACC_SNK;

	polarity = cc2 > cc1;

	if (polarity)
		rp_level = typec_get_sink_dbg_acc_rp_level(cc2, cc1);
	else
		rp_level = typec_get_sink_dbg_acc_rp_level(cc1, cc2);

	typec_set_plug_orient(tcpc, TYPEC_CC_RD, polarity);
	tcpc->typec_remote_rp_level = rp_level;

	tcpci_report_power_control(tcpc, true);
	tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
}
#else
static inline void typec_sink_dbg_acc_attached_entry(
	struct sgm41620_tcpc *tcpc)
{
	typec_custom_src_attached_entry(tcpc);
}
#endif	/* CONFIG_TYPEC_CAP_DBGACC_SNK */



/*
 * [BLOCK] Try.SRC / TryWait.SNK
 */

#if CONFIG_TYPEC_CAP_TRY_SOURCE

static inline bool typec_role_is_try_src(
	struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_role != TYPEC_ROLE_TRY_SRC)
		return false;

#if CONFIG_TYPEC_CAP_ROLE_SWAP
	if (tcpc->typec_during_role_swap)
		return false;
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

	return true;
}

static inline void typec_try_src_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_try_src_entry\n");
	TYPEC_NEW_STATE(typec_try_src);
	tcpc->typec_drp_try_timeout = false;

	tcpci_set_cc(tcpc, TYPEC_CC_RP);
	tcpc_enable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY);
}

static inline void typec_trywait_snk_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_trywait_snk_entry\n");
	TYPEC_NEW_STATE(typec_trywait_snk);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);

	tcpci_set_vconn(tcpc, false);
	tcpci_set_cc(tcpc, TYPEC_CC_RD);
	tcpci_source_vbus(tcpc,
			TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0);
	tcpc_disable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY);

	tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE);
}

static inline void typec_trywait_snk_pe_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_trywait_snk_pe_entry\n");
	typec_attach_new_unattached(tcpc);

//#if IS_ENABLED(CONFIG_USB_POWER_DELIVERY)
#if 1
	if (tcpc->typec_attach_old) {
		TYPEC_NEW_STATE(typec_trywait_snk_pe);
		return;
	}
#endif

	typec_trywait_snk_entry(tcpc);
}

#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */


#if CONFIG_TYPEC_CAP_TRY_SINK
static inline bool typec_role_is_try_sink(
	struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_role != TYPEC_ROLE_TRY_SNK)
		return false;

#if CONFIG_TYPEC_CAP_ROLE_SWAP
	if (tcpc->typec_during_role_swap)
		return false;
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

	return true;
}

static inline void typec_try_snk_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_try_snk_entry\n");
#if CONFIG_TYPEC_CAP_TRY_SINK
	TYPEC_NEW_STATE(typec_try_snk);
	tcpc->typec_drp_try_timeout = false;

	tcpci_set_cc(tcpc, TYPEC_CC_RD);
	tcpc_enable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY);
#endif
}

static inline void typec_trywait_src_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_trywait_src_entry\n");
#if CONFIG_TYPEC_CAP_TRY_SINK
	TYPEC_NEW_STATE(typec_trywait_src);
	tcpc->typec_drp_try_timeout = false;

	tcpci_set_cc(tcpc, TYPEC_CC_RP);
	tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_0V, 0);
	tcpc_enable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY);
#endif
}

#endif /* CONFIG_TYPEC_CAP_TRY_SINK */

/*
 * [BLOCK] Attach / Detach
 */

static inline void typec_cc_snk_detect_vsafe5v_entry(
	struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_cc_snk_detect_vsafe5v_entry!\n");
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);

	/* Both Rp*/
	if (!typec_check_cc_any(TYPEC_CC_VOLT_OPEN)) {	
		typec_sink_dbg_acc_attached_entry(tcpc);
		return;
	}

#if CONFIG_TYPEC_CAP_TRY_SOURCE
	if (typec_role_is_try_src(tcpc)) {
		if (tcpc->typec_state == typec_attachwait_snk) {
			typec_try_src_entry(tcpc);
			return;
		}
	}
#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */

	typec_sink_attached_entry(tcpc);
}

static inline void typec_cc_snk_detect_entry(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_cc_snk_detect_entry!\n");
	/* If Port Partner act as Source without VBUS, wait vSafe5V */
	if (tcpci_check_vbus_valid(tcpc)){
		typec_cc_snk_detect_vsafe5v_entry(tcpc);
	}
	else{
		typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SNK_VSAFE5V);
	}
}


static inline void typec_cc_src_detect_vsafe0v_entry(
	struct sgm41620_tcpc *tcpc)
{
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);

#if CONFIG_TYPEC_CAP_TRY_SINK
	if (typec_role_is_try_sink(tcpc)) {
		if (tcpc->typec_state == typec_attachwait_src) {
			typec_try_snk_entry(tcpc);
			return;
		}
	}
#endif /* CONFIG_TYPEC_CAP_TRY_SINK */
	//log_i("typec_cc_src_detect_vsafe0v_entry!!!\r\n");
	typec_source_attached_entry(tcpc);
}

static inline void typec_cc_src_detect_entry(
	struct sgm41620_tcpc *tcpc)
{
	/* If Port Partner act as Sink with low VBUS, wait vSafe0v */
	if (tcpci_check_vsafe0v(tcpc))
		typec_cc_src_detect_vsafe0v_entry(tcpc);
	else
		typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SRC_VSAFE0V);
}


static inline void typec_cc_src_remove_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_cc_src_remove_entry!\n");
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);

	tcpc->typec_is_attached_src = false;
	
#if CONFIG_TYPEC_CAP_TRY_SOURCE
	if (typec_role_is_try_src(tcpc)) {
		switch (tcpc->typec_state) {
		case typec_attached_src:
			typec_trywait_snk_pe_entry(tcpc);
			return;
		case typec_try_src:
			typec_trywait_snk_entry(tcpc);
			return;
		}
	}
#endif	/* CONFIG_TYPEC_CAP_TRY_SOURCE */

	typec_unattach_wait_pe_idle_entry(tcpc);
}

static inline void typec_cc_snk_remove_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_cc_snk_remove_entry!\n");
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);

#if CONFIG_TYPEC_CAP_TRY_SINK
	if (tcpc->typec_state == typec_try_snk) {
		typec_trywait_src_entry(tcpc);
		return;
	}
#endif	/* CONFIG_TYPEC_CAP_TRY_SINK */

	typec_unattach_wait_pe_idle_entry(tcpc);
}


/*
 * [BLOCK] CC Change (after debounce)
 */

#if CONFIG_TYPEC_CAP_DBGACC
static void typec_debug_acc_attached_with_vbus_entry(
		struct sgm41620_tcpc *tcpc)
{
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_DEBUG;
}
#endif	/* CONFIG_TYPEC_CAP_DBGACC */

static inline void typec_debug_acc_attached_entry(struct sgm41620_tcpc *tcpc)
{
//#if CONFIG_TYPEC_CAP_DBGACC
#if 0
	log_i("Debug: CC1&2 Both Rd\n");
	TYPEC_NEW_STATE(typec_debugaccessory);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DBG_VSAFE5V);

	tcpci_report_power_control(tcpc, true);
	tcpci_source_vbus(tcpc,
			TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, -1);
#endif	/* CONFIG_TYPEC_CAP_DBGACC */
}

#if CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS
static inline bool typec_audio_acc_sink_vbus(
	struct sgm41620_tcpc *tcpc, bool vbus_valid)
{
#if 0
	if (vbus_valid) {
		//tcpci_report_power_control(tcpc, true);
		tcpci_sink_vbus(tcpc,
			TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, 500);
	} else {
		tcpci_sink_vbus(tcpc,
			TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_0V, 0);
		tcpci_report_power_control(tcpc, false);
	}
#endif
	return true;
}
#endif	/* CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS */

static inline void typec_audio_acc_attached_entry(struct sgm41620_tcpc *tcpc)
{
	log_i("Audio :CC1&2 Both Ra\n");
	TYPEC_NEW_STATE(typec_audioaccessory);
	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	tcpc->typec_attach_new = TYPEC_ATTACHED_AUDIO;

//#if CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS
#if 0
	if (tcpci_check_vbus_valid(tcpc))
		typec_audio_acc_sink_vbus(tcpc, true);
#endif	/* CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS */
}

static inline bool typec_cc_change_source_entry(struct sgm41620_tcpc *tcpc)
{
	bool src_remove = false;
	//log_i("typec_cc_change_source_entry!\n");
	switch (tcpc->typec_state) {
	case typec_attached_src:
		if (typec_get_cc_res() != TYPEC_CC_VOLT_RD)
			src_remove = true;
		break;
	case typec_audioaccessory:
		if (!typec_check_cc_both(TYPEC_CC_VOLT_RA))
			src_remove = true;
		break;
#if CONFIG_TYPEC_CAP_DBGACC
	case typec_debugaccessory:
		if (!typec_check_cc_both(TYPEC_CC_VOLT_RD))
			src_remove = true;
		break;
#endif	/* CONFIG_TYPEC_CAP_DBGACC */
	default:
		if (typec_check_cc_both(TYPEC_CC_VOLT_RD))
			typec_debug_acc_attached_entry(tcpc);
		else if (typec_check_cc_both(TYPEC_CC_VOLT_RA))
			typec_audio_acc_attached_entry(tcpc);
		else if (typec_check_cc_any(TYPEC_CC_VOLT_RD))
			typec_cc_src_detect_entry(tcpc);
		else
			src_remove = true;
		break;
	}

	if (src_remove)
		typec_cc_src_remove_entry(tcpc);

	return true;
}


static inline bool typec_attached_snk_cc_change(struct sgm41620_tcpc *tcpc)
{
	log_i("typec_attached_snk_cc_change!\n");
	uint8_t cc_res = typec_get_cc_res();

	struct pd_port *pd_port = p_pd_port;

	if (cc_res != tcpc->typec_remote_rp_level) {
		log_i("RpLvl Change\n");
		tcpc->typec_remote_rp_level = cc_res;

#if CONFIG_USB_PD_REV30
		if (pd_port->pe_data.pd_connected && pd_check_rev30(pd_port)){
			log_i("pd_put_sink_tx_event!\n");
			pd_put_sink_tx_event(tcpc, cc_res);
		}
#endif	/* CONFIG_USB_PD_REV30 */

		tcpci_sink_vbus(tcpc,
				TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
	}

	return true;
}



static inline bool typec_cc_change_sink_entry(struct sgm41620_tcpc *tcpc)
{
	bool snk_remove = false;
	//log_i("typec_cc_change_sink_entry: state = %d!\n",tcpc->typec_state);
	switch (tcpc->typec_state) {
	case typec_attached_snk:
#if CONFIG_TYPEC_CAP_DBGACC_SNK
	case typec_attached_dbgacc_snk:
#endif	/* CONFIG_TYPEC_CAP_DBGACC_SNK */
		if (typec_get_cc_res() == TYPEC_CC_VOLT_OPEN)
			snk_remove = true;
		else
			typec_attached_snk_cc_change(tcpc);
		break;

#if CONFIG_TYPEC_CAP_CUSTOM_SRC
	case typec_attached_custom_src:
		if (typec_check_cc_any(TYPEC_CC_VOLT_OPEN))
			snk_remove = true;
		break;
#endif	/* CONFIG_TYPEC_CAP_CUSTOM_SRC */

	default:
		if (!typec_is_cc_open()){
			//log_i("typec_cc_change_sink_entry default!\n");
			typec_cc_snk_detect_entry(tcpc);
		}
		else
			snk_remove = true;
	}

	if (snk_remove)
		typec_cc_snk_remove_entry(tcpc);

	return true;
}


bool typec_handle_cc_changed_entry(struct sgm41620_tcpc *tcpc)
{
      log_i("[CC_Change] %d/%d\n", typec_get_cc1(), typec_get_cc2());

       tcpc->typec_attach_new = tcpc->typec_attach_old;

       if (tcpc_typec_is_act_as_sink_role(tcpc))
               typec_cc_change_sink_entry(tcpc);
       else
              typec_cc_change_source_entry(tcpc);

       typec_alert_attach_state_change(tcpc);
       return true;
}



static inline void typec_attach_wait_entry(struct sgm41620_tcpc *tcpc_dev)
{
	bool as_sink;
//#ifdef CONFIG_USB_POWER_DELIVERY
#if 1 
	struct pd_port *pd_port = p_pd_port;
#endif	/* CONFIG_USB_POWER_DELIVERY */

	if (tcpc_dev->typec_attach_old == TYPEC_ATTACHED_SNK||
	    tcpc_dev->typec_attach_old == TYPEC_ATTACHED_DBGACC_SNK) {
#if CONFIG_USB_PD_REV30
		if (pd_port->pe_data.pd_connected && pd_check_rev30(pd_port))
			pd_put_sink_tx_event(tcpc_dev, typec_get_cc_res());
#endif	/* CONFIG_USB_PD_REV30 */
		
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
		log_i("RpLvl Alert\r\n");
		return;
	}

	if (tcpc_dev->typec_attach_old ||
		tcpc_dev->typec_state == typec_attached_src) {
		tcpc_reset_typec_debounce_timer(tcpc_dev);
		//log_i("Attached, Ignore cc_attach\r\n");
		typec_enable_vconn(tcpc_dev);
		return;
	}

	switch (tcpc_dev->typec_state) {

#if CONFIG_TYPEC_CAP_TRY_SOURCE
	case typec_try_src:
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_TRYCCDEBOUNCE);
		return;

	case typec_trywait_snk:
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
		return;
#endif

#if CONFIG_TYPEC_CAP_TRY_SINK
	case typec_try_snk:	/* typec_drp_try_timeout = true */
		log_i("case typec_try_snk\r\n");
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_TRYCCDEBOUNCE);
		return;

	case typec_trywait_src:	/* typec_drp_try_timeout = unknown */
		log_i("case typec_trywait_src\r\n");
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_TRYCCDEBOUNCE);
		return;
#endif	/* CONFIG_TYPEC_CAP_TRY_SINK */

#if 1//def CONFIG_USB_POWER_DELIVERY
	case typec_unattachwait_pe:
		log_i("Force PE Idle\r\n");
		tcpc_dev->pd_wait_pe_idle = false;
		tcpc_disable_timer(tcpc_dev, TYPEC_RT_TIMER_PE_IDLE);
		typec_unattached_power_entry(tcpc_dev);
		break;
#endif
	default:
		break;
	}

	as_sink = tcpc_typec_is_act_as_sink_role(tcpc_dev);

//#if CONFIG_TYPEC_NOTIFY_ATTACHWAIT
#if 0
	tcpci_notify_attachwait_state(tcpc_dev, as_sink);
#endif	/* CONFIG_TYPEC_NOTIFY_ATTACHWAIT */

	if (as_sink){
		TYPEC_NEW_STATE(typec_attachwait_snk);
		//tcpc_dev->typec_wait_ps_change = TYPEC_WAIT_PS_SNK_VSAFE5V;		
	}
	else {
		/* Advertise Rp level before Attached.SRC Ellisys 3.1.6359 */
		TYPEC_NEW_STATE(typec_attachwait_src);
		//log_i("typec_local_rp_level = %d\r\n",tcpc_dev->typec_local_rp_level);
		tcpci_set_cc(tcpc_dev,
			TYPEC_CC_PULL(tcpc_dev->typec_local_rp_level, TYPEC_CC_RP));
		/*tcpci_set_cc(tcpc_dev,
			TYPEC_CC_PULL(0, TYPEC_CC_RP));*/
		//tcpc_dev->typec_wait_ps_change = TYPEC_WAIT_PS_SRC_VSAFE5V;
	}

	tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
}

static inline int typec_attached_snk_cc_detach(struct sgm41620_tcpc *tcpc)
{
	tcpc_reset_typec_debounce_timer(tcpc);

	if (tcpc->pd_wait_hard_reset_complete) {
		log_i("Detach_CC (HardReset)\n");
		tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE);
	} else if (p_pd_port->pe_data.pd_prev_connected) {
		log_i("Detach_CC (PD)\n");
		tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE);
	}

	return 0;
}


static inline void typec_detach_wait_entry(struct sgm41620_tcpc *tcpc_dev)
{
	//log_i("typec_detach_wait_entry\r\n");
	switch (tcpc_dev->typec_state) {
	case typec_attached_snk:
#if CONFIG_TYPEC_CAP_DBGACC_SNK
	case typec_attached_dbgacc_snk:
#endif	/* CONFIG_TYPEC_CAP_DBGACC_SNK */
		typec_attached_snk_cc_detach(tcpc_dev);
		break;

	case typec_attached_src:
    case typec_attachwait_src:
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_SRCDISCONNECT);
		break;

	case typec_audioaccessory:
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_CCDEBOUNCE);
		break;

#if CONFIG_TYPEC_CAP_TRY_SOURCE
	case typec_try_src:
		if (tcpc_dev->typec_drp_try_timeout)
			tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
		else {
			tcpc_reset_typec_debounce_timer(tcpc_dev);
			log_i("[Try] Ignore cc_detach\n");
		}
		break;
#endif	/* CONFIG_TYPEC_CAP_TRY_SOURCE */

#if CONFIG_TYPEC_CAP_TRY_SINK
	case typec_trywait_src:
		if (tcpc_dev->typec_drp_try_timeout)
			tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_TRYCCDEBOUNCE);
		else {
			tcpc_reset_typec_debounce_timer(tcpc_dev);
			log_i("[Try] Ignore cc_detach\n");
		}
		break;
#endif	/* CONFIG_TYPEC_CAP_TRY_SINK */
	default:
		tcpc_enable_timer(tcpc_dev, TYPEC_TIMER_PDDEBOUNCE);
		break;
	}
}

static inline int typec_handle_src_reach_vsafe0v(struct sgm41620_tcpc *tcpc)
{
	typec_cc_src_detect_vsafe0v_entry(tcpc);
	typec_alert_attach_state_change(tcpc);
	return 0;
}

static inline int typec_handle_src_toggle_timeout(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_TYPEC_CAP_ROLE_SWAP
	if (tcpc->typec_during_role_swap)
		return 0;
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

	if (tcpc->typec_state == typec_unattached_src) {
		typec_unattached_snk_and_drp_entry(tcpc);
		typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);
	}

	return 0;
}


int tcpc_typec_handle_vsafe0v(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_wait_ps_change != TYPEC_WAIT_PS_SRC_VSAFE0V)
		return 0;

	typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE);

	if (typec_is_drp_toggling()) {
		log_i("[Warning] DRP Toggling\n");
		return 0;
	}

	if (tcpc_typec_is_cc_open_state(tcpc))
		return 0;

#if CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY
	tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_SAFE0V_DELAY);
#else
	typec_handle_src_reach_vsafe0v(tcpc);
#endif

	return 0;
}

int typec_handle_pe_idle(struct sgm41620_tcpc *tcpc)
{
	switch (tcpc->typec_state) {

#if CONFIG_TYPEC_CAP_TRY_SOURCE
	case typec_trywait_snk_pe:
		typec_trywait_snk_entry(tcpc);
		break;
#endif

	case typec_unattachwait_pe:
		typec_unattached_entry(tcpc);
		break;

	default:
		log_i("Dummy pe_idle\n");
		break;
	}

	return 0;
}

#if CONFIG_TYPEC_CAP_ROLE_SWAP
static inline int typec_handle_role_swap_start(struct sgm41620_tcpc *tcpc)
{
	uint8_t role_swap = tcpc->typec_during_role_swap;

	if (role_swap == TYPEC_ROLE_SWAP_TO_SNK) {
		log_i("Role Swap to Sink\n");
		tcpci_set_cc(tcpc, TYPEC_CC_RD);
		tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP);
	} else if (role_swap == TYPEC_ROLE_SWAP_TO_SRC) {
		log_i("Role Swap to Source\n");
		tcpci_set_cc(tcpc, TYPEC_CC_RP);
		tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP);
	}

	return 0;
}

static inline int typec_handle_role_swap_stop(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_during_role_swap) {
		log_i("TypeC Role Swap Failed\n");
		tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_NONE;
		tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE);
	}

	return 0;
}
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */


/*
 * [BLOCK] Handle timeout event
 */

#if CONFIG_TYPEC_CAP_TRY_STATE
static inline int typec_handle_drp_try_timeout(struct sgm41620_tcpc *tcpc)
{
	bool src_detect = false, en_timer;

	tcpc->typec_drp_try_timeout = true;
	tcpc_disable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY);

	if (typec_is_drp_toggling()) {
		log_i("[Warning] DRP Toggling\n");
		return 0;
	}

	src_detect = typec_check_cc_any(TYPEC_CC_VOLT_RD);

	switch (tcpc->typec_state) {
#if CONFIG_TYPEC_CAP_TRY_SOURCE
	case typec_try_src:
		log_i("handle_drp_try_timeout :case typec_try_src\n");
		en_timer = !src_detect;
		break;
#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */

#if CONFIG_TYPEC_CAP_TRY_SINK
	case typec_trywait_src:
		log_i("handle_drp_try_timeout :case typec_trywait_src\n");
		en_timer = !src_detect;
		break;

	case typec_try_snk:
		en_timer = true;
		log_i("handle_drp_try_timeout :case typec_try_snk\n");
		if (!typec_is_cc_open()){
			log_i("tcpci_notify_attachwait_state\r\n");
			//tcpci_notify_attachwait_state(tcpc, true);
		}
		break;
#endif /* CONFIG_TYPEC_CAP_TRY_SINK */

	default:
		en_timer = false;
		break;
	}

	if (en_timer)
		tcpc_enable_timer(tcpc, TYPEC_TIMER_TRYCCDEBOUNCE);

	return 0;
}
#endif	/* CONFIG_TYPEC_CAP_TRY_STATE */

static inline int typec_handle_debounce_timeout(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_TYPEC_CAP_NORP_SRC
	if (tcpc->typec_state == typec_unattached_snk &&
	    tcpci_check_vbus_valid(tcpc) && typec_is_cc_no_res()) {
		typec_norp_src_attached_entry(tcpc);
		typec_alert_attach_state_change(tcpc);
	}
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */

	if (typec_is_drp_toggling()) {
		log_i("[Warning] DRP Toggling\n");
		return 0;
	}

	typec_handle_cc_changed_entry(tcpc);
	return 0;
}

static inline int typec_handle_error_recovery_timeout(
						struct sgm41620_tcpc *tcpc)
{
#if 1
	tcpc->pd_wait_pe_idle = false;
#endif	/* CONFIG_USB_POWER_DELIVERY */

	typec_unattach_wait_pe_idle_entry(tcpc);
	typec_alert_attach_state_change(tcpc);
	return 0;
}


int tcpc_typec_handle_timeout(struct sgm41620_tcpc *tcpc, uint32_t timer_id)
{
	int ret = 0;

	if (timer_id >= TYPEC_TIMER_START_ID &&
	    tcpc_is_timer_active(tcpc, TYPEC_TIMER_START_ID, PD_TIMER_NR)) {
		log_i("[Type-C] Ignore timer_evt\n");
		return 0;
	}

	if (timer_id == TYPEC_TIMER_ERROR_RECOVERY)
		return typec_handle_error_recovery_timeout(tcpc);
	else if (timer_id == TYPEC_RT_TIMER_STATE_CHANGE)
		return typec_alert_attach_state_change(tcpc);
	else if (tcpc_typec_is_cc_open_state(tcpc)) {
		log_i("[Open] Ignore timer_evt\n");
		return 0;
	}

#if CONFIG_TYPEC_CAP_TRY_STATE
	if (timer_id == TYPEC_TRY_TIMER_DRP_TRY)
		return typec_handle_drp_try_timeout(tcpc);
#endif	/* CONFIG_TYPEC_CAP_TRY_STATE */

#if 1
	if (tcpc->pd_wait_pr_swap_complete) {
		log_i("[PR.Swap] Ignore timer_evt\n");
		return 0;
	}
#endif	/* CONFIG_USB_POWER_DELIVERY */

	switch (timer_id) {
	case TYPEC_TIMER_CCDEBOUNCE:
	case TYPEC_TIMER_PDDEBOUNCE:
	case TYPEC_TIMER_TRYCCDEBOUNCE:
	case TYPEC_TIMER_SRCDISCONNECT:
#if CONFIG_TYPEC_CAP_NORP_SRC
	case TYPEC_TIMER_NORP_SRC:
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */
		ret = typec_handle_debounce_timeout(tcpc);
		break;

	case TYPEC_TIMER_DRP_SRC_TOGGLE:
		ret = typec_handle_src_toggle_timeout(tcpc);
		break;

#if 1
	case TYPEC_RT_TIMER_PE_IDLE:
		ret = typec_handle_pe_idle(tcpc);
		break;
#if CONFIG_USB_PD_WAIT_BC12
	case TYPEC_RT_TIMER_PD_WAIT_BC12:
		typec_handle_pd_wait_bc12(tcpc);
		break;
#endif /* CONFIG_USB_PD_WAIT_BC12 */
#endif /* CONFIG_USB_POWER_DELIVERY */

#if CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY
	case TYPEC_RT_TIMER_SAFE0V_DELAY:
		ret = typec_handle_src_reach_vsafe0v(tcpc);
		break;
#endif	/* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY */

	case TYPEC_RT_TIMER_LOW_POWER_MODE:
		typec_enter_low_power_mode(tcpc);
		break;

#if CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT
	case TYPEC_RT_TIMER_SAFE0V_TOUT:
		log_i("VSafe0V TOUT (%d)\n", tcpc->vbus_level);

		if (!tcpci_check_vbus_valid_from_ic(tcpc))
			ret = tcpc_typec_handle_vsafe0v(tcpc);
		break;
#endif	/* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */

#if CONFIG_TYPEC_CAP_ROLE_SWAP
	case TYPEC_RT_TIMER_ROLE_SWAP_START:
		typec_handle_role_swap_start(tcpc);
		break;

	case TYPEC_RT_TIMER_ROLE_SWAP_STOP:
		typec_handle_role_swap_stop(tcpc);
		break;
#endif	/* CONFIG_TYPEC_CAP_ROLE_SWAP */

	case TYPEC_RT_TIMER_DISCHARGE:
		if (!tcpc->typec_power_ctrl) {
			log_i("TYPEC_RT_TIMER_DISCHARGE\n");
			//tcpci_enable_auto_discharge(tcpc, false);
			tcpci_enable_force_discharge(tcpc, false, 0);

		}
		break;

#if CONFIG_WATER_DETECTION
	case TYPEC_RT_TIMER_WD_IN_KPOC:
		if (tcpc->wd_in_kpoc) {
			tcpci_report_usb_port_detached(tcpc);
			ret = tcpci_set_water_protection(tcpc, true);
		}
		break;
#endif /* CONFIG_WATER_DETECTION */
	}
	return ret;
}


/*
 * [BLOCK] Handle ps-change event
 */

static inline int typec_handle_vbus_present(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_wait_ps_change = %d\r\n",tcpc->typec_wait_ps_change);
#if 1
	switch (tcpc->typec_wait_ps_change) {
	case TYPEC_WAIT_PS_SNK_VSAFE5V:
		typec_cc_snk_detect_vsafe5v_entry(tcpc);
		typec_alert_attach_state_change(tcpc);
		break;
	case TYPEC_WAIT_PS_SRC_VSAFE5V:
		typec_source_attached_with_vbus_entry(tcpc);
		typec_alert_attach_state_change(tcpc);
		break;
#if CONFIG_TYPEC_CAP_DBGACC
	case TYPEC_WAIT_PS_DBG_VSAFE5V:
		typec_debug_acc_attached_with_vbus_entry(tcpc);
		typec_alert_attach_state_change(tcpc);
		break;
#endif	/* CONFIG_TYPEC_CAP_DBGACC */
	}
#endif
	return 0;
}
static inline int typec_attached_snk_vbus_absent(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_USB_PD_DIRECT_CHARGE
	if (tcpc->pd_during_direct_charge && !tcpci_check_vsafe0v(tcpc)) {
		log_i("Ignore vbus_absent(snk), DirectCharge\n");
		return 0;
	}
#endif

	if (tcpc->pd_wait_hard_reset_complete &&
	    typec_get_cc_res() != TYPEC_CC_VOLT_OPEN) {
		log_i("Ignore vbus_absent(snk), HReset & CC!=0\n");
		return 0;
	}

#if CONFIG_TYPEC_SNK_DETACH_IGNORE_VBUS
	if (tcpc->pd_exit_attached_snk_via_cc &&
	   p_pd_port->pe_data.pd_prev_connected && !typec_is_cc_no_res()) {
		log_i("Ignore vbus_absent(snk), PD & CC!=0\n");
		return 0;
	}
#endif

	typec_unattach_wait_pe_idle_entry(tcpc);
	typec_alert_attach_state_change(tcpc);

	return 0;
}


static inline int typec_handle_vbus_absent(struct sgm41620_tcpc *tcpc)
{
	//log_i("typec_handle_vbus_absent!\n");
	if (tcpc->pd_wait_pr_swap_complete) {
		log_i("[PR.Swap] Ignore vbus_absent\n");
		return 0;
	}

	switch (tcpc->typec_state) {
	case typec_attached_snk:
#if CONFIG_TYPEC_CAP_DBGACC_SNK
	case typec_attached_dbgacc_snk:
#endif	/* CONFIG_TYPEC_CAP_DBGACC_SNK */
		typec_attached_snk_vbus_absent(tcpc);
		break;
	default:
		break;
	}

	return 0;
}

int tcpc_typec_handle_ps_change(struct sgm41620_tcpc *tcpc, int vbus_level)
{
	//tcpci_notify_ps_change(tcpc, vbus_level);
	//log_i("%s vbus_level = %d\r\n",__func__,vbus_level);
	
#if CONFIG_TYPEC_CAP_NORP_SRC
	if (typec_try_norp_src(tcpc))
		return 0;
#endif	/* CONFIG_TYPEC_CAP_NORP_SRC */

#if CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS
	if (tcpc->typec_state == typec_audioaccessory) {
		return typec_audio_acc_sink_vbus(
			tcpc, vbus_level >= TCPC_VBUS_VALID);
	}
#endif	/* CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS */

	if (vbus_level >= TCPC_VBUS_VALID){
		//log_i("%s vbus_level >TCPC_VBUS_VALID =  %d\r\n",__func__,vbus_level);
		return typec_handle_vbus_present(tcpc);
	}

	return typec_handle_vbus_absent(tcpc);
}


int tcpc_typec_handle_pe_pr_swap(struct sgm41620_tcpc *tcpc)
{
	int ret = 0;

	switch (tcpc->typec_state) {
	case typec_attached_snk:
		//log_i("set typec_attached_src\r\n");
		TYPEC_NEW_STATE(typec_attached_src);
		tcpc->typec_is_attached_src = true;
		tcpc->typec_attach_new = TYPEC_ATTACHED_SRC;
		tcpci_set_cc(tcpc,
			TYPEC_CC_PULL(tcpc->typec_local_rp_level, TYPEC_CC_RP));
		break;
	case typec_attached_src:
		//log_i("set typec_attached_snk\r\n");
		TYPEC_NEW_STATE(typec_attached_snk);
		tcpc->typec_is_attached_src = false;
		tcpc->typec_attach_new = TYPEC_ATTACHED_SNK;
		tcpci_set_cc(tcpc, TYPEC_CC_RD);
		break;
	default:
#if TYPEC_STATE_INFO
		log_i("default :typec_state = %s\r\n",typec_state_name[tcpc->typec_state]);
#endif
		break;
	}

	typec_alert_attach_state_change(tcpc);
	return ret;
}

int tcpc_typec_handle_cc_change(struct sgm41620_tcpc *tcpc_dev)
{
    int ret;
    int rp_present = typec_get_rp_present_flag(p_tcpc_dev);

    ret = tcpci_get_cc(p_tcpc_dev);
    if (ret < 0)
        return ret;

#if CONFIG_TYPEC_CAP_NORP_SRC
    if (typec_try_norp_src(tcpc_dev)){
        return 0;
    }
#endif

    if (typec_is_ignore_cc_change(tcpc_dev, rp_present))
        return 0;

    if (tcpc_dev->typec_state == typec_attachwait_snk
        || tcpc_dev->typec_state == typec_attachwait_src) {
        typec_wait_ps_change(tcpc_dev, TYPEC_WAIT_PS_DISABLE);
    }

    if (typec_is_cc_attach(tcpc_dev)) {
        typec_disable_low_power_mode(tcpc_dev);
        typec_attach_wait_entry(tcpc_dev);
    } else {
        typec_detach_wait_entry(tcpc_dev);
    }

    return 0;
}


bool tcpc_typec_is_act_as_sink_role(struct sgm41620_tcpc *tcpc)
{
	bool as_sink = true;
	uint8_t cc_sum;

	switch (TYPEC_CC_PULL_GET_RES(tcpc->typec_local_cc)) {
	case TYPEC_CC_RP:
		as_sink = false;
		break;
	case TYPEC_CC_RD:
		as_sink = true;
		break;
	case TYPEC_CC_DRP:
		cc_sum = typec_get_cc1() + typec_get_cc2();
		as_sink = (cc_sum >= TYPEC_CC_VOLT_SNK_DFT);
		break;
	}
	//log_i("typec_local_cc = %d,as_sink = %d\r\n",tcpc->typec_local_cc,as_sink);
	return as_sink;
}


#if CONFIG_TYPEC_CAP_ROLE_SWAP
int tcpc_typec_swap_role(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_role < TYPEC_ROLE_DRP)
		return TCPM_ERROR_NOT_DRP_ROLE;

	if (tcpc->typec_during_role_swap)
		return TCPM_ERROR_DURING_ROLE_SWAP;

	switch (tcpc->typec_attach_old) {
	case TYPEC_ATTACHED_SNK:
		tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_TO_SRC;
		break;
	case TYPEC_ATTACHED_SRC:
		tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_TO_SNK;
		break;
	}

	if (tcpc->typec_during_role_swap) {
		log_i("TypeC Role Swap Start\n");
		tcpci_set_cc(tcpc, TYPEC_CC_OPEN);
		tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_START);
		return TCPM_SUCCESS;
	}

	return TCPM_ERROR_UNATTACHED;
}
#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */


int tcpc_typec_set_rp_level(struct sgm41620_tcpc *tcpc, uint8_t rp_lvl)
{
	switch (rp_lvl) {
	case TYPEC_RP_DFT:
	case TYPEC_RP_1_5:
	case TYPEC_RP_3_0:
		log_i("TypeC-Rp: %d\n", rp_lvl);
		tcpc->typec_local_rp_level = rp_lvl;
		break;
	default:
		log_i("TypeC-Unknown-Rp (%d)\n", rp_lvl);
		return -1;
	}

	return 0;
}

static inline void typec_error_recovery_entry(struct sgm41620_tcpc *tcpc)
{
	typec_cc_open_entry(tcpc, typec_errorrecovery);
	tcpc_reset_typec_debounce_timer(tcpc);
	tcpc_enable_timer(tcpc, TYPEC_TIMER_ERROR_RECOVERY);
}

int tcpc_typec_error_recovery(struct sgm41620_tcpc *tcpc)
{
	if (tcpc->typec_state != typec_errorrecovery)
		typec_error_recovery_entry(tcpc);

	return 0;
}
